package app

import (
	"context"
	"fmt"
	"gitee.com/zhucheer/orange/cfg"
	"gitee.com/zhucheer/orange/database"
	"gitee.com/zhucheer/orange/encrypt"
	"gitee.com/zhucheer/orange/logger"
	orangerequest "gitee.com/zhucheer/orange/request"
	"net/http"
	"time"
)

// AppSrv 路由注册接口
type AppSrv interface {
	ServeMux()
}

// 启动应用
func AppStart(appSrv AppSrv) {
	consoleWelCome()

	// 加载配置文件
	cfg.ParseParams()
	cfg.StartConfig()

	// 启动logger
	logger.NewLogger()
	// 注册session
	sessionInit()
	// 注册路由
	appSrv.ServeMux()

	// 注册mysql/redis
	database.NewMysql().RegisterAll()
	database.NewRedis().RegisterAll()

	// http请求组件
	initRequest()

	// 启动http服务
	startHttpSrv()
}

// startHttpSrv 启动http服务
func startHttpSrv() {
	appAddr := cfg.Config.GetString("app.httpAddr")
	appPort := cfg.Config.GetInt("app.httpPort")
	bindAddr := fmt.Sprintf("%s:%d", appAddr, appPort)
	httpApiMux := http.NewServeMux()

	for _, item := range routers {
		httpApiMux.HandleFunc(item.patten, handlerFunc(item))
		consoleRouter(item.method, item.patten)
	}
	fmt.Println(fmt.Sprintf("[ORANGE] \033[0;33m start http server bind %v \033[0m ", bindAddr))

	server := &http.Server{
		Addr:    bindAddr,
		Handler: httpApiMux,
	}

	go listenShutDownSign(context.Background(), server)
	err := server.ListenAndServe()
	fmt.Println(fmt.Sprintf("[ORANGE] \033[0;33m http server shutdown %v \033[0m ", err))
	logger.Critical("http server shutdown %v", err)

	exitWaitHandler.wg.Wait()

	logger.Critical("app server shutdown")
	time.Sleep(500*time.Millisecond)
}

// getAppKey 获取应用密钥
func getAppKey() string {
	defaultKey := "orange is fast frame work"
	appKey := cfg.Config.GetString("app.key")
	if appKey == "" || len(appKey) < 8 {
		logger.Error("config app.key is empty or too short")
		appKey = defaultKey
	}
	appKeyMd5 := encrypt.Md5ToLower(appKey)
	return appKeyMd5
}

// 处理HTTP方法
func handlerFunc(node routerNode) func(writer http.ResponseWriter, request *http.Request) {
	return func(writer http.ResponseWriter, request *http.Request) {
		method := request.Method
		if node.patten != request.URL.Path {
			directOutput(writer, http.StatusNotFound, []byte("Not Found"))
			return
		}
		if node.method != "ALL" && node.method != method {
			directOutput(writer, http.StatusNotFound, []byte("Not Found"))
			return
		} else {
			ctx := NewCtx(context.Background(), writer, request)
			ctx.session = sessioinStart(writer, request)
			ctx.responseBody.Reset()
			ctx.OrangeInput = orangerequest.NewInput(request, maxBodySize)

			routeFuncDo := func(next HandlerFunc) HandlerFunc {
				return func(c *Context) error {
					node.appHandler(c)
					return next(c)
				}
			}

			// 中间件依次调用
			var middleHandlerFunc HandlerFunc = routeFuncDo(func(c *Context) error {
				return nil
			})
			for _, item := range node.middlewares {
				middleFunc := item.Func()
				middleHandlerFunc = middleFunc(middleHandlerFunc)
			}

			if cfg.Config.GetBool("app.csrfVerify") == true {
				ctx.CsrfToken = startCsrfToken(ctx.Session())
				middleHandlerFunc = checkCsrfToken(middleHandlerFunc)
			}

			middleHandlerFunc(ctx)
			httpAfterDo(ctx)
		}
	}
}

// directOutput
func directOutput(writer http.ResponseWriter, code int, content []byte) {
	writer.WriteHeader(code)
	writer.Write(content)
}

// httpAfterDo http后置操作
func httpAfterDo(c *Context) error {
	c.session.SessionRelease(c.response)

	// 最后输出body
	c.response.Write(c.responseBody.Bytes())
	c.responseBody.Reset()
	return nil
}

// 输出已注册路由方法到控制台
func consoleRouter(method, patten string) {
	fmt.Println(fmt.Sprintf("[ORANGE] \033[0;33m %s\033[0m\t   %s", method, patten))
}

// 输出提示到控制台
func consoleMsg(method string, colorNum int) {
	fmt.Println(fmt.Sprintf("[ORANGE] \033[0;%dm %s\033[0m\t ", colorNum, method))
}

// 输出已注册路由方法到控制台
func consoleWelCome() {
	console := `
//////////////////////////////////////////
  _____                               
 / ___ \                              
| |   | | ____ ____ ____   ____  ____ 
| |   | |/ ___) _  |  _ \ / _  |/ _  )
| |___| | |  ( ( | | | | ( ( | ( (/ / 
 \_____/|_|   \_||_|_| |_|\_|| |\____)
                         (_____|  
//////////////////////////////////////////
///////  Fast Api Web Framework  /////////
//////////////////////////////////////////

`
	fmt.Println(fmt.Sprintf("\033[0;33m %v \033[0m", console))
}
